//
//  BBNodeViewFactory.m
//  xNose
//
//  Created by ben smith on 12/09/08.
//  Copyright 2008 __MyCompanyName__. All rights reserved.
//

#import "BBNodeViewFactory.h"
#import "BBNode.h"
#import "BBNodeView.h"

#import "BBContainerNodeView.h"
#import "BBCollectionNodeView.h"
#import "BBLeafNodeView.h"
#import "BBNodeController.h"

@implementation BBNodeViewFactory

// set a container and all of it's occupants to the positions they should be 
// at in their current state
+(void)resetContainer:(BBContainerNodeView*)containerView animate:(BOOL)animate
{
	float scale = [[BBNodeController sharedNodeController] scale];

	NSPoint snapPoint = [[BBNodeController sharedNodeController] nodeSnapPoint];
	
	float maxHeight = snapPoint.y;
	
	float titleHeight = [[NSUserDefaults standardUserDefaults] floatForKey:@"BBTitleBarHeight"];
	float viewWidth = scale * [[NSUserDefaults standardUserDefaults] floatForKey:@"BBContainerWidth"];
	float border = scale * [[NSUserDefaults standardUserDefaults] floatForKey:@"BBBorderWidth"];
	
	// which container are we?
	NSInteger index = [containerView.node.parent.children indexOfObject:containerView.node];
	// where should i be right now?
	float currentX = border + ((viewWidth + (border/2.0)) * index) + snapPoint.x;
	
	// find the current height for the current state of the nodes inside
	float newHeight =  scale * [BBNodeViewFactory viewHeightForNode:[containerView node]];
	
	// grab the frame so we can modify it
	NSRect oldFrame = [containerView frame];
	
	// calulate the proper position
	NSPoint p = NSMakePoint(currentX, maxHeight - titleHeight - ( scale * [BBNodeViewFactory viewHeightForNode:containerView.node]) );
	[containerView.node setPosition:p];

	// set our frame to the right position and height
	oldFrame.origin = containerView.node.position;
	oldFrame.size.height = newHeight;
	oldFrame.size.width = viewWidth;

	// now set my view as the top view, ready for my children node views to go on top
	NSView * superView = [containerView superview];
	[containerView removeFromSuperview];
	[superView addSubview:containerView];
	[containerView setNeedsDisplay:YES];
	// reset all the nodes inside me to their proper positions etc
	[BBNodeViewFactory setNodePositionsInContainer:[containerView node]];
	[containerView.node resetViews:animate];
	
	// set my new frame all animated like (or not)
	if (animate) {
		[[containerView animator] setFrame:[containerView centerScanRect:oldFrame]];		
	} else {
		[containerView setFrame:[containerView centerScanRect:oldFrame]];
	}
}


// generates new views for the supplied nodes
// and loads them into the supplied view
// NOTE: all node views are subviews of the same superview, there are not layers
// of views.  (ie containerviews do NOT have subviews, the collection views and leaf views just sit above them
// in the superview hierarchy)
+(void)loadNodes:(BBNode*)parent intoView:(NSView*)hostView
{
	// order is important should go like so:
	// going back from the user's eyes:
	// colection nodes
	// leaf nodes
	// container nodes
	// root node (this is added elswhere, so dont add it here)
	
	// add any containers
	if ((![parent isRoot]) && [parent isContainer]) [hostView addSubview:[BBNodeViewFactory viewForNode:parent]];

	// add the child nodes
	for (BBNode* kid in parent.children) {
		[BBNodeViewFactory loadNodes:kid intoView:hostView];
	}
	// now add myself as long as I am not a container 
	if ((![parent isRoot]) && ![parent isContainer]) [hostView addSubview:[BBNodeViewFactory viewForNode:parent]];
}

// provide a new view for the specified node
+(BBNodeView*)viewForNode:(BBNode*)aNode
{
	// check the node type and generate the specifed view
	if ([aNode isContainer]) return [BBNodeViewFactory containerViewForNode:(BBNode*)aNode];
	if ([aNode isCollection]) return [BBNodeViewFactory collectionViewForNode:(BBNode*)aNode];
	if ([aNode isLeaf]) return [BBNodeViewFactory leafViewForNode:(BBNode*)aNode];
	return nil;
}

// generate a container view
+(BBContainerNodeView*)containerViewForNode:(BBNode*)aNode;
{
	BBContainerNodeView * thisView = [[BBContainerNodeView alloc] initWithFrame:[BBNodeViewFactory frameForNode:aNode]];
	// set all my subnodes to their proper positions
	// otherwise when their nodes are made, they wil be all out of whack
	[BBNodeViewFactory setNodePositionsInContainer:aNode];
	[thisView setNode:aNode];
	aNode.isHidden = NO;
	return [thisView autorelease];
}

// generates a collection. 
+(BBCollectionNodeView*)collectionViewForNode:(BBNode*)aNode;
{
	float viewWidth = [[NSUserDefaults standardUserDefaults] floatForKey:@"BBContainerWidth"];
	// containers are as big as their kids, need to check to see if the kids are expanded or not
	// we need to make our frame special, since we dont technically contain the kid nodes
	// we never want to be as high as our 'node height' gets calculated at
	float viewHeight = [[NSUserDefaults standardUserDefaults] floatForKey:@"BBCollectionHeight"];
	NSPoint p = aNode.position;	
	NSRect frame = NSMakeRect(p.x, p.y, viewWidth, viewHeight);	
	BBCollectionNodeView * thisView = [[BBCollectionNodeView alloc] initWithFrame:frame];
	[thisView setNode:aNode];
	return [thisView autorelease];	
}

// generate a leaf node
+(BBLeafNodeView*)leafViewForNode:(BBNode*)aNode;
{
	BBLeafNodeView * thisView = [[BBLeafNodeView alloc] initWithFrame:[BBNodeViewFactory frameForNode:aNode]];
	[thisView setNode:aNode];
	return [thisView autorelease];	
}

// a frame for any given node
+(NSRect)frameForNode:(BBNode*)aNode
{
	float viewWidth = [[NSUserDefaults standardUserDefaults] floatForKey:@"BBContainerWidth"];
	// containers are as big as their kids, need to check to see if the kids are expanded or not
	float viewHeight = [BBNodeViewFactory viewHeightForNode:aNode];
	NSPoint p = aNode.position;
	return NSMakeRect(p.x, p.y, viewWidth, viewHeight);	
}

// calculates the height that this node will take up, with all of it's children
// not always the height of the node frame, and it is all dependant on whether
// the node is open or not
+(float)viewHeightForNode:(BBNode*)aNode
{
	// leafs are easy
	if ([aNode isLeaf]) {
		float leafHeight = [[NSUserDefaults standardUserDefaults] floatForKey:@"BBLeafHeight"];
		float leafBorder = [[NSUserDefaults standardUserDefaults] floatForKey:@"BBLeafBorder"];
		return (leafHeight + leafBorder);			
	}
	// otherwise, gotta calc for the kids
	float total = 0;
	if ([aNode isCollection]) {
		// a collection? add my collection height
		total += [[NSUserDefaults standardUserDefaults] floatForKey:@"BBCollectionHeight"];
		// if I am closed, then that is it!
		if (![aNode isOpen])	return total;	
	}
	
	if ([aNode isContainer]) {
		//a container? then i need to add in the extra graphics bits
		total += [[NSUserDefaults standardUserDefaults] floatForKey:@"BBTitleBarHeight"];;
		// top and bottom
		total += [[NSUserDefaults standardUserDefaults] floatForKey:@"BBBorderWidth"];;
		total += [[NSUserDefaults standardUserDefaults] floatForKey:@"BBBorderWidth"];;
	}
	
	// now add my kids
	for (BBNode* kid in aNode.children) {
		total += [BBNodeViewFactory viewHeightForNode:kid];
	}
	return total;
}

// exactly the same as height for node, but this ignore whether the collections
// are open or not
+(float)maxHeightForNode:(BBNode*)aNode
{
	if ([aNode isLeaf]) {
		float leafHeight = [[NSUserDefaults standardUserDefaults] floatForKey:@"BBLeafHeight"];
		float leafBorder = [[NSUserDefaults standardUserDefaults] floatForKey:@"BBLeafBorder"];
		return leafHeight + leafBorder;			
	}
	float total = 0;
	if ([aNode isCollection]) {
		total += [[NSUserDefaults standardUserDefaults] floatForKey:@"BBCollectionHeight"];
	}
	
	if ([aNode isContainer]) {
		total += [[NSUserDefaults standardUserDefaults] floatForKey:@"BBTitleBarHeight"];;
		// top and bottom
		total += [[NSUserDefaults standardUserDefaults] floatForKey:@"BBBorderWidth"];;
		total += [[NSUserDefaults standardUserDefaults] floatForKey:@"BBBorderWidth"];;
	}
	
	for (BBNode* kid in aNode.children) {
		total += [BBNodeViewFactory viewHeightForNode:kid];
	}
	return total;
}
		
+(void)resetNodeViewBounds:(BBNode*)aNode
{
	float scale = [[BBNodeController sharedNodeController] scale];
	float viewWidth = scale * [[NSUserDefaults standardUserDefaults] floatForKey:@"BBContainerWidth"];
	NSRect frame = [aNode.view frame];
	frame.size.width = viewWidth;
	if ([aNode isCollection]) frame.size.height = scale * [[NSUserDefaults standardUserDefaults] floatForKey:@"BBCollectionHeight"];
	if ([aNode isLeaf]) {
		float leafHeight = [[NSUserDefaults standardUserDefaults] floatForKey:@"BBLeafHeight"];
		float leafBorder = [[NSUserDefaults standardUserDefaults] floatForKey:@"BBLeafBorder"];
		frame.size.height = scale * (leafHeight + leafBorder);
	}
	[aNode.view setFrame:frame];
	for (BBNode * kid in aNode.children) {
		[BBNodeViewFactory resetNodeViewBounds:kid];
	}
}

// sets the frame positions for every node in a container
+(void)setNodePositionsInContainer:(BBNode*)container
{
	float scale = [[BBNodeController sharedNodeController] scale];
	
	if (![container isContainer]) return;
	// find each height and set the positions accordingly
	// start at the bottom
	NSPoint position = container.position;
	position.y += scale * [[NSUserDefaults standardUserDefaults] floatForKey:@"BBBorderWidth"];
	for (BBNode* collection in [container.children reverseObjectEnumerator]) {
		// if it is open, and it has kids
		NSView * superView = [collection.view superview];
		if ([collection hasChildren]) {
			// then do the kids first
			for (BBNode* leaf in [collection.children reverseObjectEnumerator]) {
				[leaf setPosition:position];
				if ([collection isOpen])
					position.y += scale *  [BBNodeViewFactory viewHeightForNode:leaf];
				// flip the leaf to the top of the subview
				[leaf.view removeFromSuperview];
				[superView addSubview:leaf.view];
			}
		}
		[collection setPosition:position];
		// flip the collection so it is above the leaves
		[collection.view removeFromSuperview];
		[superView addSubview:collection.view];	
		
		position.y += scale * [[NSUserDefaults standardUserDefaults] floatForKey:@"BBCollectionHeight"];
	}
	[BBNodeViewFactory resetNodeViewBounds:container];
}

// a bit of a silly method, but it is a central place to get this rectangle
+(NSRect)connectionButtonRectForView:(BBNodeView*)aView
{
	float inset = [[NSUserDefaults standardUserDefaults] floatForKey:@"BBConnectionInset"];
	
	return NSMakeRect(NSMinX([aView bounds]) + inset, NSMidY([aView bounds]) - 8, 23, 23);
}
@end
